#include "daemonize.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>

#define MAXLINE     4096

/*
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
 static void
 err_doit(int errnoflag, int error, const char *fmt, va_list ap)
 {
    char buf[MAXLINE];
    vsnprintf(buf, MAXLINE - 1, fmt, ap);
    if(errnoflag)
        snprintf(buf + strlen(buf), MAXLINE - strlen(buf) - 1, ": %s", strerror(error));
        strcat(buf, "\n");
        fflush(stdout); /* in case stdout and stderr are the same */
        fputs(buf, stderr);
        fflush(NULL); /* flushes all stdio output streams */
 }

/*
 * Fatal error unrelated to a system call.
 * Print a message and terminate.
 */
static void
err_quit(const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

void
daemonize(const char *cmd)
{
    int i, fd0, fd1, fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;

    /* Clear file creation mask */
    umask(0);

    /* Get maximum number of file descriptors */
    if(getrlimit(RLIMIT_NOFILE, &rl) < 0) 
        err_quit("%s: can't get file limit", cmd); 

    /* Become a session leader to lose controlling TTY */
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if(pid != 0)  /* parent */
        exit(0);
    setsid();

    /* Ensure future opens won't allocate controlling TTYs */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP, &sa, NULL) < 0)
        err_quit("%s: can't ignore SIGHUP", cmd);
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if(pid != 0)  /* parent */
        exit(0);

    /*
     * Change the current working directory to the root so
     * we won't provent file systems from being unmounted.
     */
    if(chdir("/") < 0) 
        err_quit("%s: can't change directory to /", cmd);

    /* Close all open file descriptors */
    if(rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for(i = 0; i < rl.rlim_max; i++)
        close(i);

    /* Attach file descriptos 0, 1, and 2 to /dev/null */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);


    /* Initializethe log file */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if(fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptos %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}

